#include "common/pch.h"
#include "object/camera.h"

#include <cftf/loger/syslog.h>
#include "object/player.h"
#include "object/updatefields.h"
#include "map/map.h"

namespace battle::object
{

    Camera::Camera(Player* pl) : m_owner(*pl), m_source(pl)
    {
        m_source->GetViewPoint().Attach(this);
    }

    Camera::~Camera()
    {
        // view of camera should be already reseted to owner (RemoveFromWorld -> Event_RemovedFromWorld -> ResetView)
        CFT_ASSERT(m_source == &m_owner);

        // for symmetry with constructor and way to make viewpoint's list empty
        m_source->GetViewPoint().Detach(this);
    }

    void Camera::ReceivePacket(WorldPacket* data)
    {
#if 0
        m_owner.SendDirectMessage(data);
#endif
    }

    void Camera::UpdateForCurrentViewPoint()
    {
#if 0
        m_gridRef.unlink();
#endif

        if (GridType* grid = m_source->GetViewPoint().m_grid)
        {
            grid->AddWorldObject(this);
        }

        UpdateVisibilityForOwner();
    }

    void Camera::SetView(WorldObject* obj, bool update_far_sight_field /*= true*/)
    {
        CFT_ASSERT(obj);

        if (m_source == obj)
        {
            return;
        }

        if (!m_owner.IsInMap(obj))
        {
            cftf::loger::SysLog::Instance().Error("Camera::SetView, viewpoint is not in map with camera's owner");
            return;
        }

        if (!obj->isType(TypeMask(TYPEMASK_DYNAMICOBJECT | TYPEMASK_UNIT)))
        {
            cftf::loger::SysLog::Instance().Error("Camera::SetView, viewpoint type is not available for client");
            return;
        }

        // detach and deregister from active objects if there are no more reasons to be active
        m_source->GetViewPoint().Detach(this);
        if (!m_source->IsActiveObject())
        {
            m_source->GetMap()->RemoveFromActive(m_source);
        }

        m_source = obj;

        if (!m_source->IsActiveObject())
        {
            m_source->GetMap()->AddToActive(m_source);
        }

        m_source->GetViewPoint().Attach(this);

        if (update_far_sight_field)
        {
            m_owner.SetGuidValue(PLAYER_FARSIGHT, (m_source == &m_owner ? ObjectGuid() : m_source->GetObjectGuid()));
        }

        UpdateForCurrentViewPoint();
    }

    void Camera::Event_ViewPointVisibilityChanged()
    {
        if (!m_owner.HaveAtClient(m_source))
        {
            ResetView();
        }
    }

    void Camera::ResetView(bool update_far_sight_field /*= true*/)
    {
        SetView(&m_owner, update_far_sight_field);
    }

    void Camera::Event_AddedToWorld()
    {
        GridType* grid = m_source->GetViewPoint().m_grid;
        CFT_ASSERT(grid);
        grid->AddWorldObject(this);

        UpdateVisibilityForOwner();
    }

    void Camera::Event_RemovedFromWorld()
    {
        if (m_source == &m_owner)
        {
#if 0
            m_gridRef.unlink();
#endif
            return;
        }

        ResetView();
    }

    void Camera::Event_Moved()
    {
#if 0
        m_gridRef.unlink();
#endif
        m_source->GetViewPoint().m_grid->AddWorldObject(this);
    }

    void Camera::UpdateVisibilityOf(WorldObject* target)
    {
        m_owner.UpdateVisibilityOf(m_source, target);
    }

    void Camera::UpdateVisibilityOf(WorldObject* target, UpdateData& data, std::set<WorldObject*>& vis)
    {
        m_owner.UpdateVisibilityOf(m_source, target, data, vis);
    }

    void Camera::UpdateVisibilityForOwner()
    {
#if 0
        MaNGOS::VisibleNotifier notifier(*this);
        Cell::VisitAllObjects(m_source, notifier, m_source->GetMap()->GetVisibilityDistance(), false);
        notifier.Notify();
#endif
    }

}